package com.ldodds.ot.serializer;

import java.util.HashMap;
import java.util.Map;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.ldodds.ot.ItemImpl;
import com.ldodds.ot.Item;
import com.ldodds.ot.PropertyHolder;
import com.ldodds.ot.PropertyHolderImpl;
import com.ldodds.ot.id.IdentifierRewriter;
import com.ldodds.ot.id.SimpleIdentifierRewriter;
import com.ldodds.ot.property.PropertyMapper;
import com.ldodds.ot.property.jena.PrefixMappingPropertyMapper;
import com.ldodds.ot.util.StatementUtils;

public class ItemSerializerImpl implements ItemSerializer 
{
	private IdentifierRewriter rewriter;
	private StatementMapper statementMapper;
	private PropertyMapper propertyMapper;
	private boolean isConfigured;
	private Map<String, Item> items;
	
	public ItemSerializerImpl() {
		this( new StatementMapperImpl(), null, "");
	}

	public ItemSerializerImpl(StatementMapper statementMapper, PropertyMapper propertyMapper, String baseURI) {
		this(new SimpleIdentifierRewriter(baseURI), statementMapper, propertyMapper, baseURI);
	}

	public ItemSerializerImpl(IdentifierRewriter rewriter, StatementMapper statementMapper, PropertyMapper propertyMapper, String baseURI) {
		this.statementMapper = statementMapper;
		this.propertyMapper = propertyMapper;
		this.rewriter = rewriter;
		items = new HashMap<String, Item>();
	}
	
	public Item serialize(Model model, String identifier) {
		return serialize(model, model.getResource( identifier ) );
	}

	public Item serialize(Model model, Resource resource) {
		if ( resource.isAnon() ) {
			throw new ResourceSerializationException("Cannot serialize bnode with this method");
		}
		configure(model);
		Item item = getItem(resource);
		if (item != null) {
			return item;
		} else {
			item = createItem(resource);
		}
		StmtIterator iter = resource.listProperties();
		while ( iter.hasNext() ) {
			Statement statement = iter.nextStatement();
			if ( isObjectOfStatementAList(statement) ) {
				statementMapper.populateMapFromList(item, statement);
			}
			else if ( isObjectOfStatementACollection(statement) ) {
				statementMapper.populateMapFromCollection(item, statement);
			}
			else {
				statementMapper.populateMap(item, statement);	
			}
		}
		return item;
	}

	private Item getItem(Resource resource) {
		String uri = resource.getURI();
		return items.get(uri);
	}

	private Item createItem(Resource resource) {
		String uri = resource.getURI();
		Item item = new ItemImpl( resource.getURI(), rewriter );
		items.put(uri, item);
		return item;
	}
	
	public PropertyHolder serializeBlankNode(Model model, Resource resource) {
		if ( !resource.isAnon() ) {
			throw new ResourceSerializationException("Should only serialize bnodes with this method");
		}
		configure(model);
		PropertyHolder properties = new PropertyHolderImpl();
		StmtIterator iter = resource.listProperties();
		while ( iter.hasNext() ) {
			statementMapper.populateMap(properties, iter.nextStatement() );
		}
		return properties;
	}

	private void configure(Model model) {
		if (isConfigured) {
			return;
		}
		if (propertyMapper == null) {
			propertyMapper = new PrefixMappingPropertyMapper(model);
		}
		if (statementMapper == null) {
			statementMapper = new StatementMapperImpl();
		}
		statementMapper.setPropertyMapper(propertyMapper);
		statementMapper.setStatementVisitor( new StatementVisitor(this) );
		isConfigured = true;
	}
	
	protected boolean isObjectOfStatementAList(Statement statement) {
		return StatementUtils.isObjectOfStatementAList(statement);
	}
	
	protected boolean isObjectOfStatementACollection(Statement statement) {
		return StatementUtils.isObjectOfStatementACollection(statement);
	}
	
}
